using System;
using System.Collections.Generic;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Caches;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.UnitTestExplorer;
using JetBrains.Shell;
using JetBrains.Util;

namespace JamesKovacs.ReSharper.UnitTestSupport.VstsUnit {
    public abstract class VstsUnitElementBase : UnitTestElement {
        private readonly ProjectModelElementEnvoy m_Project;
        private readonly string m_TypeName;

        public VstsUnitElementBase(IUnitTestProvider provider, VstsUnitElementBase parent, IProject project, string typeName) : base(provider, parent) {
            if((project == null) && !Shell.Instance.IsTestShell) {
                throw new ArgumentNullException("project");
            }
            if(typeName == null) {
                throw new ArgumentNullException("typeName");
            }
            if(project != null) {
                m_Project = new ProjectModelElementEnvoy(project);
            }
            m_TypeName = typeName;
        }

        public override bool Equals(object obj) {
            if(base.Equals(obj)) {
                VstsUnitElementBase elementBase = (VstsUnitElementBase)obj;
                if(Equals(elementBase.m_Project, m_Project)) {
                    return (elementBase.m_TypeName == m_TypeName);
                }
            }
            return false;
        }

        protected ITypeElement GetDeclaredType() {
            ITypeElement typeElement;
            IProject project = GetProject();
            if(project == null) {
                return null;
            }
            PsiManager manager = PsiManager.GetInstance(project.GetSolution());
            ReadLockCookie readLockCookie = ReadLockCookie.Create();
            try {
                typeElement = manager.GetDeclarationsCache(DeclarationsCacheScope.ProjectScope(project, true), true).GetTypeElementByCLRName(m_TypeName);
            } finally {
                readLockCookie.Dispose();
            }
            return typeElement;
        }

        public override UnitTestElementDisposition GetDisposition() {
            IDeclaredElement element = GetDeclaredElement();
            if((element == null) || !element.IsValid()) {
                return UnitTestElementDisposition.ourInvalidDisposition;
            }
            List<UnitTestElementLocation> locations = new List<UnitTestElementLocation>();
            foreach(IDeclaration declaration in element.GetDeclarations()) {
                IFile file = declaration.GetContainingFile();
                if(file != null) {
                    locations.Add(new UnitTestElementLocation(file.ProjectItem, declaration.GetNameRange(), declaration.GetDocumentRange().TextRange));
                }
            }
            return new UnitTestElementDisposition(locations, this);
        }

        public override int GetHashCode() {
            int result = base.GetHashCode();
            result = (0x1d*result) + m_Project.GetHashCode();
            return ((0x1d*result) + m_TypeName.GetHashCode());
        }

        public override UnitTestNamespace GetNamespace() {
            CLRTypeName clrTypeName = new CLRTypeName(m_TypeName);
            return new UnitTestNamespace(clrTypeName.NamespaceName);
        }

        public override IProject GetProject() {
            return (m_Project.GetValidProjectElement() as IProject);
        }

        public override IList<IProjectItem> GetProjectItems() {
            ITypeElement declaredType = GetDeclaredType();
            if(declaredType == null) {
                return EmptyArray<IProjectItem>.Instance;
            }
            return declaredType.GetProjectFiles();
        }

        public override string GetTypeClrName() {
            return m_TypeName;
        }

        public abstract IList<UnitTestTask> GetTaskSequence(IList<UnitTestElement> explicitElements);
    }
}